Skip to content

[codex] Add Windows TSF IME insertion backend#210

Merged
H-Chris233 merged 46 commits into
Open-Less:mainfrom
millionart:main
May 4, 2026
Merged

[codex] Add Windows TSF IME insertion backend#210
H-Chris233 merged 46 commits into
Open-Less:mainfrom
millionart:main

Conversation

@millionart
Copy link
Copy Markdown
Contributor

@millionart millionart commented May 3, 2026

User description

Summary

Adds a Windows TSF IME backend for OpenLess dictation insertion and packages/registers it with the Windows build.

This routes Windows insertion through TSF first, with the existing non-TSF fallback path controlled by the fallback setting. The PR also packages the native IME DLLs into the Windows MSI and registers/unregisters them during install/uninstall.

Updates Included

  • Adds the native Windows TSF text service DLL under openless-all/app/windows-ime.
  • Adds Rust-side Windows IME session preparation, IPC submission, profile restore, registration health checks, and protocol helpers.
  • Adds Settings UI/i18n for Windows TSF backend status and the non-TSF fallback toggle.
  • Adds MSVC packaging scripts and WiX wiring for MSI registration.
  • Builds and registers both x64 and Win32 IME DLLs.
  • Uses fresh side-by-side registration staging paths so running host apps cannot lock the next build output.

Fixes Included

  • Battle.net: supports 32-bit host processes by building and registering the Win32 IME DLL.
  • Microsoft Word: retries TF_E_SYNCHRONOUS rejections with an async TSF edit session.
  • Microsoft Word: moves the caret to the end of inserted text after commit by collapsing the committed range and setting selection.

Validation

  • node scripts/windows-package-msvc.test.mjs
  • x64 OpenLessIme.dll Release build: 0 warnings, 0 errors
  • Win32 OpenLessIme.dll Release build: 0 warnings, 0 errors
  • Local Battle.net manual test: text commits successfully
  • Local Microsoft Word manual test: text commits successfully and caret lands after inserted text

PR Type

Enhancement, Tests


Description

  • Add Windows TSF IME insertion backend for dictation

    • Route insertion through a temporary text service via named pipe IPC
    • Fall back to SendInput or clipboard if TSF fails or is disabled
  • Introduce IME session lifecycle management

    • Prepare, activate, and restore the IME profile per dictation session
    • Guard against stale sessions and cancel races
  • Expose Windows IME status and settings

    • Add get_windows_ime_status command and allow_non_tsf_insertion_fallback preference
    • Wire registration health checks (COM, TIP, categories) into the UI
  • Package and register native IME DLLs for both x64 and Win32

    • Add MSVC build/registration scripts and WiX MSI integration
    • Handle side‑by‑side registration staging to avoid lock conflicts
  • Extend smoke tests and add unit tests for IME components


Diagram Walkthrough

flowchart LR
  A["Dictation Session"] -- "prepare profile" --> B["Capture active IME"]
  B -- "activate OpenLess TIP" --> C["TSF IME ready"]
  C -- "send text over pipe" --> D["Wait for commit"]
  D -- "commit succeeds" --> E["Insertion done"]
  D -- "commit fails / timeout" --> F["Fallback: Unicode SendInput"]
  F -- "fallback fails" --> G["Last resort: clipboard paste"]
  E -- "restore previous profile" --> H["Session complete"]
  F -- "restore previous profile" --> H
  G -- "restore previous profile" --> H
Loading

File Walkthrough

Relevant files
Enhancement
11 files
coordinator.rs
Route dictation insertion through Windows TSF IME with fallback and
session‑aware resource management
+696/-106
windows_ime_session.rs
Implement TSF IME session preparation, submission, and profile
restoration controller
+253/-0 
windows_ime_profile.rs
Manage Windows TSF input profiles: capture, activate, restore OpenLess
text service
+730/-0 
windows_ime_ipc.rs
Define named‑pipe IPC protocol and async submit/receive for TSF text
insertion
+430/-0 
windows_ime_protocol.rs
Define serializable pipe messages and pipe naming conventions for IME
IPC
+173/-0 
insertion.rs
Add Windows Unicode SendInput inserter and clipboard‑restore fallback
methods
+66/-6   
types.rs
Add allow_non_tsf_insertion_fallback preference and Windows IME status
types
+42/-2   
lib.rs
Register new Windows IME modules and `get_windows_ime_status` command
+28/-9   
commands.rs
Expose `get_windows_ime_status` Tauri command                       
+6/-1     
text_service.cpp
Implement the core TSF text service and edit session commit logic
+332/-0 
ipc_client.cpp
Named‑pipe client that receives submit requests and reports results
+511/-0 
Formatting
2 files
selection.rs
Formatting adjustments (macOS and Windows paste helpers) 
+4/-3     
hotkey.rs
Minor formatting and line‑break cleanup for global hotkey handlers
+20/-11 
Tests
1 files
windows-smoke-suite.ps1
Extend smoke test matrix with win32edit target and direct insertion
naming
+2/-2     
Configuration changes
6 files
windows-package-msvc.ps1
MSVC packaging script for Windows IME DLLs and MSI registration
+316/-0 
windows-ime-build.ps1
Build script for x64 and Win32 IME DLLs                                   
+82/-0   
windows-ime-register.ps1
Register IME COM server and TSF profiles in HKLM                 
+88/-0   
windows-ime-unregister.ps1
Unregister IME COM registration and TSF profiles                 
+136/-0 
registry.cpp
COM/TIP category registration helpers for the IME DLL       
+307/-0 
openless-ime.wxs
WiX installer fragment to package and register the IME DLLs
+54/-0   
Additional files
26 files
2026-05-01-windows-temporary-tsf-ime.md +2191/-0
2026-05-01-windows-temporary-tsf-ime-design.md +143/-0 
windows-package-msvc.cmd +8/-0     
windows-package-msvc.test.mjs +114/-0 
windows-preflight.ps1 +63/-1   
windows-real-asr-insertion-smoke.ps1 +76/-7   
Cargo.toml +7/-2     
tauri.conf.json +11/-0   
en.ts +12/-0   
zh-CN.ts +12/-0   
ipc.ts +13/-0   
types.ts +15/-0   
Settings.tsx +48/-0   
OpenLessIme.sln +27/-0   
OpenLessIme.vcxproj +159/-0 
OpenLessIme.def +6/-0     
class_factory.cpp +75/-0   
class_factory.h +20/-0   
dllmain.cpp +54/-0   
edit_session.cpp +125/-0 
edit_session.h +43/-0   
guids.h +21/-0   
ipc_client.h +37/-0   
registry.h +6/-0     
resource.rc +33/-0   
text_service.h +54/-0   

millionart added 30 commits May 1, 2026 18:05
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

Persistent review updated to latest commit 31045f7

@H-Chris233
Copy link
Copy Markdown
Collaborator

请继续看reviewer给出的建议并作出相应更改

@appergb
Copy link
Copy Markdown
Collaborator

appergb commented May 4, 2026

hhhhhh

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 104793cf18

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread openless-all/app/src-tauri/src/windows_ime_ipc.rs Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 2554259

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 104793c

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e040caebf9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

saved: Option<&ImeProfileSnapshot>,
openless_profile_is_current: bool,
) -> ProfileRestoreDecision {
if saved.is_some() && openless_profile_is_current {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore saved IME profile after partial activation failure

activate_openless_profile mutates state with ChangeCurrentLanguage before later activation steps can fail; when that happens, restore_decision skips restore whenever OpenLess is not currently active. This leaves the user on a changed keyboard/input profile even though the OpenLess session never became usable. The restore decision should account for failed activation paths with a saved snapshot, not only the "OpenLess still active" case.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit e040cae

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 77eb569

@H-Chris233
Copy link
Copy Markdown
Collaborator

PR Reviewer Guide 🔍

(Review updated until commit 77eb569)

Here are some key observations to aid the review process:
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
Recommended focus areas for review

Stale restore

restore_prepared_windows_ime_session restores the saved TSF profile as soon as an old session cleans up, but it never checks whether that session is still the active one. If the user cancels a dictation and starts a new one before the old async continuation resumes, the older cleanup can restore the previous profile over the new session's active OpenLess profile and break the in-flight insertion.

还要改一下(

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 1de10d3

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e2c79598a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
}
discard_startup_resources_for_session(inner, session_id);
restore_prepared_windows_ime_session(inner, session_id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore IME snapshot before exposing Idle phase

abort_recording_with_error flips state.phase to Idle before attempting restore_prepared_windows_ime_session. Because restore is guarded by state.session_id, a new dictation can start in that gap, increment the session id, and cause this restore call to discard the old prepared snapshot. In that race, the user’s pre-session IME profile is never restored and the next session can snapshot the already-switched OpenLess profile as baseline. Move restore ahead of publishing Idle (or otherwise make this path restore unconditionally for the failed session) to avoid leaking the IME switch across sessions.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 9e2c795

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec16958d4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1734 to +1744
let state = inner.state.lock();
let prepared = {
let mut slot = inner.prepared_windows_ime_session.lock();
take_current_prepared_windows_ime_session_for_restore(
&mut slot,
session_id,
state.session_id,
)
};
if let Some(prepared) = prepared {
inner.windows_ime.restore_session(prepared);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Release session mutex before IME restore call

restore_prepared_windows_ime_session keeps inner.state locked while calling inner.windows_ime.restore_session(...), which performs COM/TSF operations (is_openless_profile_active and potentially restore_profile) and can block on the OS input stack. While that call is in flight, all other paths that need state (hotkey edges, cancel, phase transitions) are serialized behind it, so a slow or hung restore can freeze session control. Capture state.session_id first, drop the lock, then run restore to avoid holding the global session mutex across external blocking work.

Useful? React with 👍 / 👎.

@H-Chris233 H-Chris233 self-assigned this May 4, 2026
@H-Chris233 H-Chris233 merged commit 4b53310 into Open-Less:main May 4, 2026
2 checks passed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit ec16958

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants